﻿#include "Skinnedmesh.h"
#include <glad/glad.h>
#include <glm/mat4x4.hpp>
#include <glm/vec3.hpp>
#include "MeshData.h"
#include "BoneWeight.h"

namespace DYGraphics
{
	SkinnedMesh::SkinnedMesh(): 
		mData(nullptr),
		mSkeleton(nullptr),
		mAnimVertices(nullptr),
		vao(-1), vbo(-1), ebo(-1) { }

	SkinnedMesh::SkinnedMesh(MeshData* pMeshData, const SkeletonPtr& pSkeleton): 
		mData(pMeshData), 
		mSkeleton(pSkeleton),
		mAnimVertices(nullptr),
		vao(-1), vbo(-1), ebo(-1) { }

	SkinnedMesh::~SkinnedMesh() 
	{
		if (vao != -1) glDeleteVertexArrays(1, &vao);
		if (vbo != -1) glDeleteBuffers(1, &vbo);
		if (ebo != -1) glDeleteBuffers(1, &ebo);

		mSkeleton = nullptr;
		if (mData != nullptr) 
		{
			mData->Destroy();
			mData = nullptr;
		}

		if (mAnimVertices != nullptr)
		{
			delete[] mAnimVertices;
			mAnimVertices = nullptr;
		}
	}

	glm::vec4 SkinnedMesh::Evaluate(glm::vec4 position, int boneIdx, float boneWeight)
	{
		Bone* bone = mSkeleton->GetBone(boneIdx);
		return (bone->mGlobalMat * bone->mOffsetMat * position) * boneWeight;
	}

	void SkinnedMesh::Animation(AnimationFrame* frames)
	{
		mSkeleton->AppyAnimFrame(frames);

		for (int i = 0; i < mData->vertexCnt; i++)
		{
			Vertex vertex = mData->vertices[i];
			BoneWeight boneWeight = mData->boneWeights[i];

			// 先只处理顶点位置，法线之后处理
			glm::vec4 animPos = glm::vec4(0, 0, 0, 0);
			glm::vec4 position = glm::vec4(vertex.position[0], vertex.position[1], vertex.position[2], 1.0f);
			if (boneWeight.boneWeight1 != 0)
				animPos += Evaluate(position, boneWeight.boneIndex1, boneWeight.boneWeight1);
			if (boneWeight.boneWeight2 != 0)
				animPos += Evaluate(position, boneWeight.boneIndex2, boneWeight.boneWeight2);
			if (boneWeight.boneWeight3 != 0)
				animPos += Evaluate(position, boneWeight.boneIndex3, boneWeight.boneWeight3);
			if (boneWeight.boneWeight4 != 0)
				animPos += Evaluate(position, boneWeight.boneIndex4, boneWeight.boneWeight4);

			mAnimVertices[i].position[0] = animPos.x;
			mAnimVertices[i].position[1] = animPos.y;
			mAnimVertices[i].position[2] = animPos.z;
		}
	}

	void SkinnedMesh::Apply()
	{
		int vertexCnt = mData->vertexCnt;
		int indiceCnt = mData->indiceCnt;
		unsigned int* indices = mData->indices;
		Vertex* vertices = mData->vertices;

		// 先复制mesh的顶点数据到动态数据
		mAnimVertices = new Vertex[mData->vertexCnt];
		for (int i = 0; i < mData->vertexCnt; i++)
		{
			Vertex vertex;
			vertex.position[0] = vertices[i].position[0];
			vertex.position[1] = vertices[i].position[1];
			vertex.position[2] = vertices[i].position[2];
			vertex.normal[0] = vertices[i].normal[0];
			vertex.normal[1] = vertices[i].normal[1];
			vertex.normal[2] = vertices[i].normal[2];
			vertex.tangent[0] = vertices[i].tangent[0];
			vertex.tangent[1] = vertices[i].tangent[1];
			vertex.tangent[2] = vertices[i].tangent[2];
			vertex.texcoord[0] = vertices[i].texcoord[0];
			vertex.texcoord[1] = vertices[i].texcoord[1];
			mAnimVertices[i] = vertex;
		}

		glGenVertexArrays(1, &vao);
		glGenBuffers(1, &vbo);
		glGenBuffers(1, &ebo);

		glBindVertexArray(vao);

		glBindBuffer(GL_ARRAY_BUFFER, vbo);
		glBufferData(GL_ARRAY_BUFFER, vertexCnt * sizeof(Vertex), &mAnimVertices[0], GL_DYNAMIC_DRAW);

		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, indiceCnt * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW);

		glEnableVertexAttribArray(0);
		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position));

		glEnableVertexAttribArray(1);
		glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, normal));

		glEnableVertexAttribArray(2);
		glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, tangent));

		glEnableVertexAttribArray(3);
		glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, texcoord));

		glBindVertexArray(0);
	}

	void SkinnedMesh::Draw()
	{
		glBindVertexArray(vao);
		glBindBuffer(GL_ARRAY_BUFFER, vbo);
		glBufferSubData(GL_ARRAY_BUFFER, 0, mData->vertexCnt * sizeof(Vertex), &mAnimVertices[0]);
		glDrawElements(GL_TRIANGLES, mData->indiceCnt, GL_UNSIGNED_INT, 0);
		glBindVertexArray(0);
	}
}
